// ----------------------------------------------------------------------------
// mutex.h
// ----------------------------------------------------------------------------
/*
 Description:
 基本ミューテクスクラス.
 ----------------------------------------------------------------
 使い方.
 
 排他制御を行いたい場所で、MutexLockerクラスを利用して排他制御を行う.
 ミューテクスのインスタンスは排他制御区間よりも以前にあらかじめ作成しておく事.
 
 たとえば、あるスコープの範囲をミューテクスを使って排他処理を行いたい場合、以下のように記述する.
 
 Mutex m("foo");
 ...
 {
   MutexLocker locker(mutex);
   ... // This is critical section...
 }
 
 このように、RAIIオブジェクトを通じて排他制御を行う事でミューテクスのアンロックし忘れを防ぐ.

 */
#pragma once
#include <fw/base/core/assertion.h>
#include <fw/base/core/noncopyable.h>

namespace fw
{
	class MutexImpl;
	
	/**
	 * 標準的なミューテクスオブジェクト.
	 */
	class Mutex : private ::fw::NonCopyable
	{
	public:
		
		Mutex(const char* name);
		
		~Mutex();
		
	protected:
		
		template<typename, bool> friend class MutexLockerTemplate;
		
		void					Lock();
		
		bool					TryLock();
		
		void					Unlock();
		
	private:
		
		MutexImpl*				m_impl;
		
		bool					m_locked;
		
		Mutex(); // 名前無しのミューテクスは作成できません. 
		
	}; // class MutexT 

	/**
	 * ミューテクスをロックするためのRAIIインターフェイス.
	 */
	template<typename T, bool TryPolicy> class MutexLockerTemplate : private ::fw::NonCopyable
	{
	public:
		
		MutexLockerTemplate( T& mutex, bool lockOnCreate = true )
		: m_mutex ( mutex )
		, m_locked( lockOnCreate )
		{
			if( !lockOnCreate )
			{
				return;
			}
			if( TryPolicy )
			{
				m_locked = m_mutex.TryLock();
			}
			else
			{
				m_mutex.Lock();
			}
		}
		
		~MutexLockerTemplate()
		{
			if( IsLocked() )
			{
				Unlock();
			}
		}
		
		void Lock()
		{
			FW_ASSERT( m_locked == false );
			
			m_mutex.Lock();
			m_locked = true;
		}
		
		bool TryLock()
		{
			FW_ASSERT( m_locked == false );
			
			if( m_mutex.TryLock() )
			{
				m_locked = true;
			}
			return m_locked;
		}
		
		void Unlock()
		{
			FW_ASSERT( m_locked == true );
			
			m_mutex.Unlock();
			m_locked = false;
		}
		
		bool IsLocked()
		{
			return m_locked;
		}
		
	private:
		
		T&								m_mutex;
		bool 							m_locked;
		
	}; // class MutexLockerTemplate 
	
	/* Lockを利用するMutex RAIIオブジェクト. */
	typedef MutexLockerTemplate< Mutex,   false >	MutexLocker;
	typedef MutexLockerTemplate< Mutex,   true >	MutexTryLocker;

} // namespace fw 
